const fs = require("fs");
const fsAsync = require("fs").promises;
const path = require("path");
const postcss = require('postcss');
const valueParser = require('postcss-value-parser');
const traverse = require("@babel/traverse").default;
const deepcopy = require('deepcopy');
const tinycolor = require('tinycolor2');


function getPseudoClassName(classNames, selector) {
  const pseudoClassName = classNames['hover-focus-and-other-states']
  const name = selector.split(':').pop()
  return pseudoClassName[`&:${name}`] || pseudoClassName[`&::${name}`]
}
function getClassNameKey(classNames, prop, value, ratio) {
  const key = Object.keys(classNames).find(key => {
    if (!key.includes(prop) || key.includes("\n")) {
      return "";
    }
    const [remValue, pxValue] = getValue(key);
    return (
      pxValue * ratio + "px" === value ||
      pxValue * ratio === value ||
      remValue * ratio + "rem" === value
    );
  });
  return key;
}
function getRealValue(str, ratio) {
  const realValue = str.replace(/\d+(px|rem)/g, match => {
    const parts = match.split(/(px|rem)/);
    const number = parseInt(parts[0], 10);
    const unit = parts[1];
    const dividedNumber = number / ratio;
    return dividedNumber + unit;
  });
  return realValue;
}
function getValue(str) {
  const regex = /(\d+(?:\.\d+)?)rem|\/\*\s*(\d+)px\s*\*\//;
  const matches = str.match(regex);
  if (matches) {
    const remValue = matches[1] || "0";
    const pxMatch = str.match(/\/\*\s*(\d+)px\s*\*\//);
    const pxValue = pxMatch ? pxMatch[1] : "0";
    return [remValue, pxValue];
  } else {
    return ["0", "0"];
  }
}
function findParentClassNameNode(path, parentName, attribute) {
  const findedParent = path.find((parentPath) => {
    const openingElement = parentPath.node.openingElement;
    function setConsequentAndAlternateValue(path, consequent, alternate) {
      if (consequent && consequent.value) {
        const classNames = consequent.value.split(' ')
        if (classNames.includes(parentName)) {
          return true
        }
      }
      if (alternate && alternate.value) {
        const classNames = alternate.value.split(' ')
        if (classNames.includes(parentName)) {
          return true
        }
      }
    }
    return openingElement && openingElement.attributes.some((attr) => {
      const { name, value } = attr
      if (name && name.name === attribute) {
        const type = value && value.type
        if (type === "StringLiteral" && value.value) {
          const isParent = value.value.split(' ').some((i) => i === parentName)
          if (isParent) {
            return true
          }
        }
        if (type === 'JSXExpressionContainer') {
          const expression = value.expression
          const { consequent, alternate, expressions } = expression
          if (setConsequentAndAlternateValue(path, consequent, alternate)) {
            return true
          }

          if (expression.type === 'TemplateLiteral') {
            for (const i of expression.quasis) {
              if (i.value.raw) {
                const classNames = i.value.raw.split(' ')
                if (classNames.includes(parentName)) {
                  return true
                }
              }
            }
          }
          if (expressions) {
            for (const i of expressions) {
              const { consequent, alternate } = i
              if (i.type === 'ConditionalExpression') {
                if (setConsequentAndAlternateValue(path, consequent, alternate)) {
                  return true
                }
              }
              if (i.type === 'LogicalExpression') {
                const { right } = i
                if (right.value) {
                  const classNames = right.value.split(' ')
                  if (classNames.includes(parentName)) {
                    return true
                  }
                }
              }
            }
          }
        }
      }
    });
  });
  if (findedParent) {
    return true
  } else {
    return false
  }
}
function isInNested(path, names, attribute) {
  for (const name of names) {
    const isFind = findParentClassNameNode(path, name.slice(1), attribute)
    if (!isFind) {
      return false
    }
  }
  return true
}
async function accessFile(file, extensions) {
  try {
    const extension = extensions[0]
    const filePath = path.join(path.dirname(file), path.parse(file).name + '.' + extension)
    await fsAsync.access(filePath, fsAsync.constants.F_OK);
    return [filePath, extension];
  } catch (error) {
    extensions = extensions.slice(1)
    if (extensions.length > 0) {
      return accessFile(file, extensions);
    } else {
      return [];
    }
  }
}
const accessFileSyncfromCache = (function () {
  const cache = {}
  return (file, extensions) => {
    if (cache[file]) {
      return cache[file]
    }
    const result = accessFileSync(file, extensions)
    if (result.length > 0) {
      cache[file] = result
    }
    return result
  }
})()
function accessFileSync(file, extensions) {
  try {
    const extension = extensions[0]
    const filePath = path.join(path.dirname(file), path.parse(file).name + '.' + extension)
    fs.accessSync(filePath, fs.constants.F_OK);
    return [filePath, extension];
  } catch (error) {
    extensions = extensions.slice(1)
    if (extensions.length > 0) {
      return accessFileSync(file, extensions);
    } else {
      return [];
    }
  }
}
function isValidColor(colorStr) {
  const color = tinycolor(colorStr)
  if (color.isValid()) {
    return color
  }
  return false
}
function isColorMethed(str) {
  return /^(#|rgb|rgba|hsl|hsla|hwb)/.test(str)
}
function colorToHax(colorStr) {
  const color = isValidColor(colorStr)
  if (color) {
    if (color.getAlpha() > 0) {
      return color.toHex8String()
    }
    return color.toHexString()
  }
  return colorStr
}
function colorToRgb(colorStr) {
  const color = isValidColor(colorStr)
  if (color) {
    return color.toRgbString()
  }
  return colorStr
}
function splitArray(arr, splitValue) {
  const splitIndex = arr.indexOf(splitValue);
  if (splitIndex === -1) {
    return [arr, []];
  }

  const leftArr = arr.slice(0, splitIndex);
  const rightArr = arr.slice(splitIndex + 1);
  return [leftArr, rightArr];
}
function isInAtRule(decl) {
  if (decl.parent.type == 'root') {
    return false
  }
  if (decl.parent.type === 'atrule') {
    return true
  }
  return isInAtRule(decl.parent)
}
function isPseudoRule(selector) {
  return selector.includes(':')
}
function generateCSSRule(atruleName, atruleParams, ruleNodes) {
  const result = `@${atruleName} ${atruleParams}{${ruleNodes.map(ruleNode => {
    const ruleSelector = ruleNode.selector;
    let rules = ''
    ruleNode.nodes.forEach((n) => {
      rules += `${n.prop}:${n.value};`
    })
    return `${ruleSelector}{${rules}}`;
  }).join('')}}`;

  return result;
}
function getKeyframesRules(css) {
  let keyframesRules = '';
  let keyframesName = '';
  const root = postcss.parse(css);
  root.walkAtRules('keyframes', (atRule) => {
    keyframesName = atRule.params;
    keyframesRules += `@keyframes ${keyframesName}{`;

    atRule.walkRules((rule) => {
      keyframesRules += `${rule.selector}{`;
      rule.walkDecls((decl) => {
        const parsedValue = valueParser(decl.value).toString();
        keyframesRules += `${decl.prop}:${parsedValue};`;
      });

      keyframesRules += '}';
    });

    keyframesRules += '}';
  });

  return [keyframesName, keyframesRules];
}
function convertAnimationToTailwind(classNames, { prop, value, keyframesAtRules }) {
  const propClassNames = classNames[prop]
  const keys = Object.keys(propClassNames)
  let className = ''
  for (const key of keys) {
    const declaration = key.split('\n')[0]
    if (declaration === `${prop}: ${value};`) {
      const [keyframesName, keyframesRules] = getKeyframesRules(key)
      if (keyframesRules === keyframesAtRules[keyframesName]) {
        className = declaration
      }
      break;
    }
  }
  if (className) {
    return { animation: className }
  }
}
function convertColorToTailwind(value) {
  const ast = valueParser(value)
  const nodes = ast.nodes
  const color = nodes
    .filter((n) => n.type === 'word' || n.type === 'function')
    .map((n) => {
      const { type, value } = n
      if (type === 'word') {
        return value
      }
      if (type === 'function') {
        if (isColorMethed(value)) {
          const colors = n.nodes.filter((n) => n.type === 'word').map((n) => n.value);
          return `${value}(${colors.join(',')})`
        }
      }
    }).join('')
  return {
    'color': `text-[${color}]`
  }
}
function convertPaddingOrMarginToTailwind(propClassNames, value, prop, propType, ratio) {
  const ast = valueParser(value)
  const nodes = ast.nodes.filter((n) => n.type === 'word' || n.type === 'function').map((n) => {
    if (n.type === 'word') {
      return n.value
    }
    if (n.type === 'function') {
      return `${n.value}(${n.nodes.filter((n) => n.type === 'word').map((n) => n.value).join('_')})`
    }
  })

  const length = nodes.length;
  const [t, r, b, l] = nodes;

  function getClassName(direction, value) {
    let className = "";
    if (value === "0") {
      className = `${propType}${direction}-0`;
    } else {
      const key = getClassNameKey(propClassNames, prop, value, ratio);
      className = key
        ? `${propType}${direction}-${propClassNames[key].split("-")[1]}`
        : `${propType}${direction}-[${value}]`;
    }
    return className;
  }

  switch (length) {
    case 1:
      if (t === "0") {
        return { [prop]: `${propType}-0` };
      }
      return { [prop]: `${propType}-[${t}]` };
    case 2:
      return {
        [`[${prop}-y]`]: getClassName("y", t),
        [`[${prop}-x]`]: getClassName("x", r),
      };
    case 3:
      return {
        [`[${prop}-top]`]: getClassName("t", t),
        [`[${prop}-x]`]: getClassName("x", r),
        [`[${prop}-bottom]`]: getClassName("b", b),
      };
    case 4:
      return {
        [`[${prop}-top]`]: getClassName("t", t),
        [`[${prop}-right]`]: getClassName("r", r),
        [`[${prop}-bottom]`]: getClassName("b", b),
        [`[${prop}-left]`]: getClassName("l", l),
      };
  }
}
function convertTransformToTailwind(classNames, { prop, value }) {
  const declaration = `${prop}: ${value};`;
  const ast = valueParser(value)
  const nodes = ast.nodes
  let valuesSize = nodes.filter((n) => n.type === 'word' || n.type === 'function').length
  const o = {}
  nodes.forEach((node) => {
    const { value, nodes, type } = node
    if (type === 'function') {
      if (!['scale', 'scaleX', "scaleY", 'rotate', 'skew', 'skewX', 'skewY', 'translate', 'translateX', 'translateY'].includes(value)) {
        return ''
      }
      const scale = classNames["scale"][declaration];
      const vals = nodes.filter((n) => n.type === 'word').map((n) => n.value)
      const val = vals.join(' ')
      if (value === "scale") {
        o['scale'] = scale || `scale-[${val}]`;
        --valuesSize
      }
      if (value === "scaleX") {
        o['scale-x'] = scale || `scale-x-[${val}]`;
        --valuesSize
      }
      if (value === "scaleY") {
        o['scale-y'] = scale || `scale-y-[${val}]`;
        --valuesSize
      }

      if (value === "rotate") {
        o['rotate'] = classNames["rotate"][declaration] || `rotate-[${val}]`;
        --valuesSize
      }

      const skew = classNames["skew"][declaration];
      if (value === "skew") {
        o['skew-x'] = skew || `skew-x-[${vals[0]}]`;
        --valuesSize
        if (vals.length > 1) {
          o['skew-y'] = skew || `skew-y-[${val[1]}]`;
          --valuesSize
        }
      }

      if (value === "skewX") {
        o['skew-x'] = skew || `skew-x-[${val}]`;
        --valuesSize
      }
      if (value === "skewY") {
        o['skew-y'] = skew || `skew-y-[${val}]`;
        --valuesSize
      }
      const translate = classNames["translate"][declaration];
      if (value === "translate") {
        o['translate-x'] = translate || `translate-x-[${vals[0]}]`;
        --valuesSize
        if (vals.length > 1) {
          o['translate-y'] = skew || `translate-y-[${vals[1]}]`;
          --valuesSize
        }
      }
      if (value === "translateX") {
        o['translate-x'] = `translate-x-[${vals[0]}]`;
        --valuesSize
      }
      if (value === "translateY") {
        o['translate-y'] = `translate-Y-[${vals[0]}]`;
        --valuesSize
      }
    }
  })

  if (valuesSize > 0) {
    return ''
  }

  return o
}
function convertOutlineToTailwind(classNames, prop, value) {
  const ast = valueParser(value)
  const nodes = ast.nodes
  let valuesSize = nodes.filter((n) => n.type === 'word' || n.type === 'function').length
  const outlineWidth = classNames["outline-width"];
  const outlineStyle = classNames["outline-style"];
  const outlineColor = classNames["outline-color"];
  const o = {}
  nodes.forEach((node) => {
    const { type, value } = node
    if (type === 'function') {
      if (isColorMethed(value)) {
        const color = node.nodes.filter((node) => node.type === 'word').map((node) => node.value).join(',');
        o['outline-color'] = outlineColor[`outline-color: ${colorToHax(`${value}(${color})`)};`] || `outline-[${color}]`
        --valuesSize
      }
    }
    if (type === 'word') {
      if (!isNaN(parseFloat(value))) {
        o['outline-width'] = outlineWidth[`outline-width: ${value};`] || `outline-[${value}]`
        --valuesSize
      }

      if (["solid", "dashed", "dotted", "double", "none"].includes(value)) {
        o['outline-style'] = outlineStyle[`outline-style: ${value};`] || `outline-[${value}]`
        --valuesSize
      }
      if (isValidColor(value)) {
        o['outline-color'] = outlineColor[`outline-color: ${colorToHax(`${value}(${value})`)};`] || `outline-[${value}]`
        --valuesSize
      }
    }
  })

  if (valuesSize > 0) {
    return ''
  }

  return o;
}
function convertBorderClassToTailwind(classNames, prop, value) {
  const ast = valueParser(value)
  const nodes = ast.nodes
  let valuesSize = nodes.filter((n) => n.type === 'word' || n.type === 'function').length
  const directionMap = {
    "border-top": "top",
    "border-right": "right",
    "border-bottom": "bottom",
    "border-left": "left",
    "border": true
  };
  const direction = directionMap[prop];
  const borderWidth = classNames["border-width"];
  const borderColor = classNames["border-color"];
  const shortDirection = direction === true ? '-' : `-${direction[0]}-`
  const dire = direction === true ? '-' : `-${direction}-`
  const o = {}
  nodes.forEach((node) => {
    const { type, value } = node
    if (type === 'function') {
      if (isColorMethed(value)) {
        const colors = node.nodes.filter((node) => node.type === 'word').map((node) => node.value);
        o[`border${dire}color`] = borderColor[`border${dire}color: ${`${value}(${colors.join(' ')})`};`] ||
          `border${shortDirection}[${colorToHax(`${value}(${colors.join(',')})`)}]`
        --valuesSize
      }
    }
    if (type === 'word') {
      if (!isNaN(parseFloat(value))) {
        o[`border${dire}width`] = borderWidth[`border${dire}width: ${value};`] || `border${shortDirection}[${value}]`
        --valuesSize
      }

      if (["solid", "dashed", "dotted", "double", "hidden", "none"].includes(value)) {
        // 不支持 border-b-hidden 等
        o[`border-style`] = `border-${value}`
        --valuesSize
      }
      if (isValidColor(value)) {
        o[`border${dire}color`] = borderColor[`border${dire}color: ${value};`] || `border${shortDirection}[${value}]`
        --valuesSize
      }
    }
  })

  if (valuesSize > 0) {
    return ''
  }

  return o;
}
function convertLinearGradientToTailwind(nodes) {
  const angle = nodes[0].value;
  const colorStops = nodes.filter((n) => n.type !== 'div').slice(1).map(node => {
    const { type, value } = node
    if (type === 'function' && isColorMethed(value)) {
      const functionArgs = node.nodes.filter(n => n.type === 'word').map((n) => n.value).join(',');
      return `${value}(${functionArgs})`;
    } else {
      const colorAndPosition = value.replace(" ", '_');
      return colorAndPosition;
    }
  });
  const result = colorStops.flatMap((item, index) => {
    if (item === '_' && index > 0 && index < colorStops.length - 1) {
      return [`${colorStops[index - 1]}_${colorStops[index + 1]}`];
    } else {
      return [];
    }
  });
  return `bg-[linear-gradient(${angle},${result.join(",")})]`;
}
function convertBackgroundSizeToTailwind(value) {
  const ast = valueParser(value)
  const nodes = ast.nodes
  const bgSize = nodes.filter((n) => n.type === 'word' && !isNaN(parseFloat(n.value))).map((n) => n.value)
  return {
    'background-size': `bg-[length:${bgSize.join('_')}]`
  }
}
function convertBoxShadowToTailwind(value) {
  const ast = valueParser(value)
  const nodes = ast.nodes
  const shadow = nodes
    .filter((n) => n.type === 'word' || n.type === 'function')
    .map((n) => {
      const { type, value } = n
      if (type === 'word') {
        return value
      }
      if (type === 'function') {
        if (isColorMethed(value)) {
          const colors = n.nodes.filter((n) => n.type === 'word').map((n) => n.value);
          return `${value}(${colors.join(',')})`
        }
      }
    }).join('_')
  return {
    'box-shadow': `shadow-[${shadow}]`
  }
}
function convertBackgroundToTailwind(classNames, { value }) {
  const ast = valueParser(value)
  const nodes = ast.nodes
  let valuesSize = nodes.filter((n) => n.type === 'word' || n.type === 'function').length
  const o = {}
  let flag = true
  nodes.forEach((node) => {
    const { type, value } = node
    if (type === 'function') {
      if (isColorMethed(value)) {
        const colors = node.nodes.filter((n) => n.type === 'word').map((n) => n.value);
        const className = classNames['background-color'][`${value}(${colors.join(' ')})`] || `bg-[${colorToHax(`${value}(${colors.join(',')})`)}]`
        o['background-color'] = className
        --valuesSize
      }
      // 处理图像
      if (value == 'url') {
        const values = node.nodes.filter((n) => n.type === 'string').map((n) => n.value).join('');
        const tailClassName = classNames['background-image'][`background-image: url(${value});`] || `bg-[url(${values})]`
        o['background-image'] = tailClassName
        --valuesSize
      }
      if (value == 'linear-gradient') {
        const tailClassName = convertLinearGradientToTailwind(node.nodes)
        o['background-image'] = tailClassName
        --valuesSize
      }
    }
    if (type === 'word') {
      // 处理颜色(非rgb 或者rgba) 尝试转换成rgb或者rgba 匹配tailWindcss, 匹配失败降级bg-[value]
      if (isValidColor(value)) {
        const color = colorToRgb(value)
        const tailClassName = classNames['background-color'][`background-color: ${color};`] || `bg-[${value}]`
        o['background-color'] = tailClassName
        --valuesSize
      }
      // 处理重复
      if (['repeat', 'no-repeat', 'repeat-x', 'repeat-y', 'round', 'space'].includes(value)) {
        const tailClassName = classNames['background-repeat'][`background-repeat: ${value};`]
        o['background-repeat'] = tailClassName
        --valuesSize
      }

      // 处理位置可枚举（非数值）
      if (!o['background-position']) {
        const positions = nodes.filter(({ value, type }) => {
          if (type === 'word') {
            return value === 'top' || value === 'right' || value === 'bottom' || value === 'left' || value === 'center'
          }
        })
        if (positions.length > 0) {
          const bgPosition = classNames['background-position']
          const tailClassName = bgPosition[`background-position: ${positions.join(' ')};`] || bgPosition[`background-position: ${positions.reverse().join(' ')};`];
          o['background-position'] = tailClassName
          --valuesSize
        }
      }
      // 处理Background Origin和Background Clip
      if (['border-box', 'padding-box', 'content-box'].includes(value)) {
        if (!o['background-origin']) {
          const tailClassName = classNames['background-origin'][`background-origin: ${value};`]
          o['background-origin'] = tailClassName
          --valuesSize
        } else {
          const tailClassName = classNames['background-clip'][`background-clip: ${value};`]
          o['background-clip'] = tailClassName
          --valuesSize
        }
      }
      // 处理background-attachment
      if (value === 'fixed' || value === 'local' || value === 'scroll') {
        const tailClassName = classNames['background-attachment'][`background-attachment: ${value};`]
        o['background-attachment'] = tailClassName
        --valuesSize
      }
      // 处理background-clip: text;
      if (value === 'text') {
        o[`background-clip: ${value};`] = 'bg-clip-text'
        --valuesSize
      }

      // 处理大小（可枚举）
      if (['cover', 'auto', 'contain'].includes(value)) {
        const tailClassName = classNames['background-size'][`background-size: ${value};`]
        o['background-size'] = tailClassName
        --valuesSize
      }
    }
    // 处理位置可枚举（数值类型）
    if (type === 'div' && value === '/' && flag) {
      flag = false
      const values = nodes.filter((n) => n.type === 'div' && n.value === '/' || n.type === 'word' && !isNaN(parseFloat(n.value))).map((n) => n.value)
      const arr = splitArray(values, '/');
      const [bgPos, bgSize] = arr;
      if (bgPos.length > 0) {
        o['background-position'] = `bg-[${bgPos.join('_')}]`
      }
      if (bgSize.length > 0) {
        o['background-size'] = `bg-[length:${bgSize.join('_')}]`
      }
      valuesSize -= arr.length
    }
  })

  if ((!o['background-position'] || !o['background-size']) && flag) {
    const values = nodes.filter((n) => n.type === 'word' && !isNaN(parseFloat(n.value))).map((n) => n.value)
    const length = values.length
    if (length > 0) {
      if (length === 1) {
        o['background-position'] = `bg-[${values[0]}]`
      }
      if (length === 2) {
        if (o['background-position']) {
          o['background-size'] = `bg-[length:${values.join('_')}]`
        } else {
          o['background-position'] = `bg-[${values.join('_')}]`
        }
      }
      valuesSize -= length
    }
  }

  if (valuesSize > 0) {
    return ''
  }
  return o
}
function convertFlexFlowToTailwind(prop, value) {
  const ast = valueParser(value)
  const nodes = ast.nodes
  const o = {}
  const values = nodes.filter((n) => n.type === 'word')
  let valuesSize = values.length
  values.forEach((node) => {
    const { value } = node
    switch (value) {
      case 'row':
        o["flex-row"] = "flex-row"
        --valuesSize
        break;
      case 'row-reverse':
        o['flex-row-reverse'] = "flex-row-reverse"
        --valuesSize
        break;
      case 'column':
        o['flex-col'] = "flex-col"
        --valuesSize
        break;
      case 'column-reverse':
        o['flex-col-reverse'] = "flex-col-reverse"
        --valuesSize
        break;
      case 'wrap':
        o['flex-nowrap'] = "flex-wrap"
        --valuesSize
        break;
      case 'nowrap':
        o['flex-nowrap'] = "flex-nowrap"
        --valuesSize
        break;
      case 'wrap-reverse':
        o['flex-wrap-reverse'] = "flex-wrap-reverse"
        --valuesSize
        break;
    }
  })
  if (valuesSize > 0) {
    return ''
  }
  return o
}
function convertTextDecorationToTailwind(classNames, value) {
  const ast = valueParser(value)
  const nodes = ast.nodes.filter((n) => n.type === 'word' || n.type === 'function')
  let valuesSize = nodes.length
  const thickness = classNames["text-decoration-thickness"];
  const style = classNames["text-decoration-style"];
  const color = classNames["text-decoration-color"];
  const line = classNames['text-decoration']
  const o = {}
  nodes.forEach((node) => {
    const { type, value } = node
    if (type === 'function') {
      if (isColorMethed(value)) {
        const color = node.nodes.filter((node) => node.type === 'word').map((node) => node.value).join(',');
        o['text-decoration-color'] = color[`text-decoration-color: ${colorToHax(`${value}(${color})`)};`] || `decoration-[${color}]`
        --valuesSize
      }
    }
    if (type === 'word') {
      if (!isNaN(parseFloat(value)) || value === 'auto' || value === 'from-font') {
        o['text-decoration-thickness'] = thickness[`text-decoration-thickness: ${value};`] || `decoration-[${value}]`
        --valuesSize
      }

      if (["solid", "dashed", "dotted", "double", "wavy"].includes(value)) {
        o['text-decoration-style'] = style[`text-decoration-style: ${value};`]
        --valuesSize
      }
      if (["underline", "overline", "line-through", "none"].includes(value)) {
        o['text-decoration-line'] = line[`text-decoration-line: ${value};`]
        --valuesSize
      }
      if (isValidColor(value)) {
        o['text-decoration-color'] = color[`text-decoration-color: ${colorToHax(`${value}(${value})`)};`] || `text-decoration-color-[${value}]`
        --valuesSize
      }
    }
  })

  if (valuesSize > 0) {
    return ''
  }

  return o;
}
function convertTransitionToTailwind(classNames, value) {
  const property = classNames['transition-property'];
  const duration = classNames['transition-duration'];
  const delay = classNames['transition-delay'];
  const timing = classNames['transition-timing-function']
  const o = {}
  const ast = valueParser(value)
  const nodes = ast.nodes.filter((n) => n.type === 'word' || n.type === 'function')
  let valuesSize = nodes.length

  const values = nodes.map((node) => {
    const { type, value } = node
    if (type === 'function') {
      const className = `ease-[${value}(${node.nodes.filter((n) => n.type === 'word').map((n) => n.value).join(',')})]`;
      o['transition-timing-function'] = timing[`transition-timing-function: ${value};`] || className
      --valuesSize
      return className
    }

    if (type === 'word') {
      if (!isNaN(parseFloat(value))) {
        if (!o['transition-duration']) {
          o['transition-duration'] = duration[`transition-duration: ${value};`] || `duration-[${value}]`
          --valuesSize
        } else {
          o['transition-delay'] = delay[`transition-delay: ${value};`] || `delay-[${value}]`
          --valuesSize
        }
      }
      if (value === 'linear') {
        const className = timing[`transition-timing-function: ${value};`]
        if (className) {
          o['transition-timing-function'] = className
          --valuesSize
        }
      }
      if (value === 'none') {
        const className = property[`transition-property: none;`]
        if (className) {
          o['transition-property'] = className
          --valuesSize
        }
      }
      return value
    }
  })

  if (valuesSize > 0) {
    return { transition: `transition-[${values.join("_")}]` }
  }

  return o;
}
function convertFilterToTailwind(classNames, value) {
  const ast = valueParser(value)
  const nodes = ast.nodes.filter((n) => n.type === 'function')
  let valuesSize = nodes.length
  const blur = classNames["blur"];
  const brightness = classNames["brightness"];
  const contrast = classNames["contrast"];
  const dropShadow = classNames["drop-shadow"];
  const grayscale = classNames["grayscale"];
  const hueRotate = classNames["hue-rotate"];
  const invert = classNames["invert"];
  const saturate = classNames["saturate"];
  const sepia = classNames["sepia"];
  const o = {}
  function getDropShadowValue(nodes) {
    return nodes.map((n) => {
      return `drop-shadow(${n.nodes.filter((n) => n.type === 'word' || n.type === 'function')
        .map((n) => {
          if (n.type === 'word') {
            return n.value
          }
          if (n.type === 'function') {
            return `${n.value}(${n.nodes.map((n) => {
              if (n.value == '/') {
                return ` / `
              }
              return n.value
            }).join('')})`
          }
        }).join(' ')})`
    }).join(' ')
  }

  function getDropShadowDefaultValue(nodes) {
    return nodes.map((n) => {
      return n.nodes.filter((n) => n.type === 'word' || n.type === 'function')
        .map((n) => {
          if (n.type === 'word') {
            return n.value
          }
          if (n.type === 'function') {
            return `${n.value}(${n.nodes
              .filter(((n) => n.type === 'word'))
              .map((n) => {
                return n.value
              }).join(',')})`
          }
        }).join('_')
    }).join(' ')
  }

  nodes.forEach((node) => {
    const { value, nodes } = node
    const nv = nodes.filter((n) => n.type === 'word')[0].value
    const val = nv.startsWith('0.') ? nv.slice(1) : nv
    switch (value) {
      case 'blur':
        o['filter'] = blur[`filter: blur(${val});`] || `blur-[${[val]}]`
        --valuesSize
        break;
      case 'brightness':
        o['filter'] = brightness[`filter: brightness(${val});`] || `brightness-[${[val]}]`
        --valuesSize
        break;
      case 'contrast':
        o['filter'] = contrast[`filter: contrast(${val});`] || `contrast-[${[val]}]`
        --valuesSize
        break;
      case 'grayscale':
        o['filter'] = grayscale[`filter: grayscale(${val});`] || `grayscale-[${[val]}]`
        --valuesSize
        break;
      case 'hue-rotate':
        o['filter'] = hueRotate[`filter: hue-rotate(${val});`] || `hue-rotate-[${[val]}]`
        --valuesSize
        break;
      case 'invert':
        o['filter'] = invert[`filter: invert(${val});`] || `invert-[${[val]}]`
        --valuesSize
        break;
      case 'saturate':
        o['filter'] = saturate[`filter: saturate(${val});`] || `saturate-[${[val]}]`
        --valuesSize
        break;
      case 'sepia':
        o['filter'] = sepia[`filter: sepia(${val});`] || `sepia-[${[val]}]`
        --valuesSize
        break;
    }
  })
  const dropShadowNodes = nodes.filter((n) => n.value === 'drop-shadow')
  if (dropShadowNodes.length > 0) {
    const className = dropShadow[`filter: ${getDropShadowValue(dropShadowNodes)};`]
    if (className) {
      o['filter'] = className
      valuesSize -= dropShadowNodes.length
    } else if (dropShadowNodes.length === 1) {
      o['filter'] = `drop-shadow-[${getDropShadowDefaultValue(dropShadowNodes)}]`
      valuesSize -= dropShadowNodes.length
    } else {
      // 自定义不支持多个
    }
  }

  if (valuesSize > 0) {
    return ''
  }

  return o;
}
function convertBackdropFilterToTailwind(classNames, value) {
  const ast = valueParser(value)
  const nodes = ast.nodes.filter((n) => n.type === 'function')
  let valuesSize = nodes.length
  const blur = classNames["backdrop-blur"];
  const brightness = classNames["backdrop-brightness"];
  const contrast = classNames["backdrop-contrast"];
  const grayscale = classNames["backdrop-grayscale"];
  const hueRotate = classNames["backdrop-hue-rotate"];
  const invert = classNames["backdrop-invert"];
  const opacity = classNames["backdrop-opacity"];
  const saturate = classNames["backdrop-saturate"];
  const sepia = classNames["backdrop-sepia"];
  const o = {}
  nodes.forEach((node) => {
    const { value, nodes } = node
    const nv = nodes.filter((n) => n.type === 'word')[0].value
    const val = nv.startsWith('0.') ? nv.slice(1) : nv
    switch (value) {
      case 'blur':
        o['backdrop-filter'] = blur[`backdrop-filter: blur(${val});`] || `backdrop-blur-[${[val]}]`
        --valuesSize
        break;
      case 'brightness':
        o['backdrop-filter'] = brightness[`backdrop-filter: brightness(${val});`] || `backdrop-brightness-[${[val]}]`
        --valuesSize
        break;
      case 'contrast':
        o['backdrop-filter'] = contrast[`backdrop-filter: contrast(${val});`] || `backdrop-contrast-[${[val]}]`
        --valuesSize
        break;
      case 'grayscale':
        o['backdrop-filter'] = grayscale[`backdrop-filter: grayscale(${val});`] || `backdrop-grayscale-[${[val]}]`
        --valuesSize
        break;
      case 'hue-rotate':
        o['backdrop-filter'] = hueRotate[`backdrop-filter: hue-rotate(${val});`] || `backdrop-hue-rotate-[${[val]}]`
        --valuesSize
        break;
      case 'invert':
        o['backdrop-filter'] = invert[`backdrop-filter: invert(${val});`] || `backdrop-invert-[${[val]}]`
        --valuesSize
        break;
      case 'opacity':
        o['backdrop-filter'] = opacity[`backdrop-filter: opacity(${val});`] || `backdrop-opacity-[${[val]}]`
        --valuesSize
        break;
      case 'saturate':
        o['backdrop-filter'] = saturate[`backdrop-filter: saturate(${val});`] || `backdrop-saturate-[${[val]}]`
        --valuesSize
        break;
      case 'sepia':
        o['backdrop-filter'] = sepia[`backdrop-filter: sepia(${val});`] || `backdrop-sepia-[${[val]}]`
        --valuesSize
        break;
    }
  })

  if (valuesSize > 0) {
    return ''
  }

  return o;
}
function checkIsEmptySelector(path, classNames, emptyRules, targetAttribute) {
  const emptySelectors = Object.keys(emptyRules)
    .map((key) => {
      if (emptyRules[key]) {
        const selector = emptyRules[key].selector.slice(1)
        if (classNames.includes(selector)) {
          return selector
          // 删除是不能检查嵌套的
          // if (isInNested(path, emptyRules[key].nested, targetAttribute)) {
          //   return selector
          // }
        }
      }
    }).filter(Boolean)
  return emptySelectors
}
function clearEmptyClassName(ast, emptyRules, targetAttribute) {
  function setConsequentAndAlternateValueOnExit(consequent, alternate) {
    if (consequent && consequent.value) {
      const classNames = consequent.value.split(' ')
      const emptySelector = checkIsEmptySelector(path, classNames, emptyRules, targetAttribute)
      const value = classNames.filter((item) => !emptySelector.includes(item)).join(' ')
      consequent.value = value
    }
    if (alternate && alternate.value) {
      const classNames = alternate.value.split(' ')
      const emptySelector = checkIsEmptySelector(path, classNames, emptyRules, targetAttribute)
      const value = classNames.filter((item) => !emptySelector.includes(item)).join(' ')
      alternate.value = value
    }
  }
  traverse(ast, {
    JSXAttribute(path) {
      const { node } = path;
      const { name, value } = node
      if (name && name.name === targetAttribute) {
        const type = value && value.type
        if (type === "StringLiteral") {
          if (value.value) {
            const classNames = value.value.split(" ");
            const emptySelector = checkIsEmptySelector(path, classNames, emptyRules, targetAttribute)
            value.value = classNames.filter((item) => !emptySelector.includes(item)).join(' ')
          }
        }
        if (type === 'JSXExpressionContainer') {
          const expression = value.expression
          const { consequent, alternate, expressions } = expression
          setConsequentAndAlternateValueOnExit(path, consequent, alternate)
          if (expression.type === 'TemplateLiteral') {
            expression.quasis.forEach((i) => {
              if (i.value.raw) {
                const classNames = i.value.raw.split(' ')
                const emptySelector = checkIsEmptySelector(path, classNames, emptyRules, targetAttribute)
                const value = classNames.filter((item) => !emptySelector.includes(item)).join(' ')
                i.value.raw = value
              }
            })
          }
          if (expressions) {
            expressions.forEach((item) => {
              const { consequent, alternate } = item
              if (item.type === 'ConditionalExpression') {
                setConsequentAndAlternateValueOnExit(path, consequent, alternate)
              }
              if (item.type === 'LogicalExpression') {
                const { right } = item
                if (right.value) {
                  const classNames = right.value.split(' ')
                  const emptySelector = checkIsEmptySelector(path, classNames, emptyRules, targetAttribute)
                  const value = classNames.filter((item) => !emptySelector.includes(item)).join(' ')
                  right.value = value
                }
              }
            })
          }
        }
      }
    },
  });
}
function getNestedSelectorsAndSelectorKey(classNames, selector) {
  let selectors = selector.split(',').map((i) => i.replace('\n', ''))
  return selectors.map((selector) => {
    const selectors = selector.split(' ')
    const nested = selectors.slice(0, -1)
    let selectorName = selectors[selectors.length - 1]
    let pseudoName = ''
    if (isPseudoRule(selectorName)) {
      pseudoName = getPseudoClassName(classNames, selectorName)
      selectorName = selectorName.split(':')[0]
    }
    return {
      key: selector,
      nested,
      pseudoName,
      selector: selectorName,
    }
  })
}
function getTailwindClassName(classNames, { prop, value, ratio, keyframesAtRules }) {
  const realValue = getRealValue(value, ratio);
  const propClassNames = classNames[prop];

  if (!propClassNames) {
    return ''
  }

  const declaration = `${prop}: ${realValue};`;
  const className = propClassNames[declaration];
  if (className) {
    return { [prop]: className };
  }
  const key = getClassNameKey(propClassNames, prop, value, ratio);
  if (propClassNames[key]) {
    return { [prop]: propClassNames[key] };
  }
  const varClassName = {
    "opacity": { "opacity": `opacity-[${value}]` },
    "line-height": { "line-height": `leading-[${value}]` },
    "letter-spacing": { "letter-spacing": `tracking-[${value}]` },
    "font-size": { "font-size": `text-[${value}]` },
    "background-color": { "background-color": `bg-[${value}]` },
    "content": { "content": `content-[${value.replace(/\"/g, "'")}]` },
    "background-image": { "background-image": `bg-[${value.replace(/\"/g, "'")}]` },
    "z-index": { "z-index": `z-[${value}]` },
    "top": { "top": `top-[${value.replace(/\s/g, '_')}]` },
    "right": { "right": `right-[${value.replace(/\s/g, '_')}]` },
    "bottom": { "bottom": `bottom-[${value.replace(/\s/g, '_')}]` },
    "left": { 'left': `left-[${value.replace(/\s/g, '_')}]` },
    "width": { "width": `w-[${value.replace(/\s/g, '_')}]` },
    "max-width": { "max-width": `max-w-[${value.replace(/\s/g, '_')}]` },
    "max-height": { "max-height": `max-h-[${value.replace(/\s/g, '_')}]` },
    "height": { "height": `h-[${value.replace(/\s/g, '_')}]` },
    "min-height": { "min-height": `min-h-[${value.replace(/\s/g, '_')}]` },
    "min-width": { "min-width": `min-w-[${value.replace(/\s/g, '_')}]` },
    "margin-top": { "margin-top": `mt-[${value.replace(/\s/g, '_')}]` },
    "margin-right": { "margin-right": `mr-[${value.replace(/\s/g, '_')}]` },
    "margin-bottom": { "margin-bottom": `mb-[${value.replace(/\s/g, '_')}]` },
    "margin-left": { "margin-left": `ml-[${value.replace(/\s/g, '_')}]` },
    "padding-top": { "padding-top": `pt-[${value.replace(/\s/g, '_')}]` },
    "padding-right": { "padding-right": `pr-[${value.replace(/\s/g, '_')}]` },
    "padding-bottom": { "padding-bottom": `pb-[${value.replace(/\s/g, '_')}]` },
    "padding-left": { "padding-left": `pl-[${value.replace(/\s/g, '_')}]` },
    "border-radius": { "border-radius": `rounded-[${value.replace(/\s/g, '_')}]` },
    "color": () => convertColorToTailwind(value),
    "box-shadow": () => convertBoxShadowToTailwind(value),
    "background-size": () => convertBackgroundSizeToTailwind(value),
    "flex-flow": () => convertFlexFlowToTailwind(classNames, value),
    "margin": () => convertPaddingOrMarginToTailwind(classNames[prop], value, prop, "m", ratio),
    "padding": () => convertPaddingOrMarginToTailwind(classNames[prop], value, prop, "p", ratio),
    transform: () => convertTransformToTailwind(classNames, { prop, value, ratio }),
    animation: () => convertAnimationToTailwind(classNames, { prop, value, keyframesAtRules }),
    "background": () => convertBackgroundToTailwind(classNames, { prop, value }),
    "border-top": () => convertBorderClassToTailwind(classNames, "border-top", value),
    "border-right": () => convertBorderClassToTailwind(classNames, "border-right", value),
    "border-bottom": () => convertBorderClassToTailwind(classNames, "border-bottom", value),
    "border-left": () => convertBorderClassToTailwind(classNames, "border-left", value),
    "border": () => convertBorderClassToTailwind(classNames, "border", value),
    "outline": () => convertOutlineToTailwind(classNames, "border", value),
    'text-decoration': () => convertTextDecorationToTailwind(classNames, value),
    'transition': () => convertTransitionToTailwind(classNames, value),
    'filter': () => convertFilterToTailwind(classNames, value),
    'backdrop-filter': () => convertBackdropFilterToTailwind(classNames, value),

  };
  return typeof varClassName[prop] === 'function' ? varClassName[prop]() : varClassName[prop];
}
function getClassNames(path, nodeClassNames, selectors, targetAttribute) {
  const arr = []
  nodeClassNames.forEach(item => {
    for (let key in selectors) {
      if (selectors[key].selector === `.${item}`) {
        if (isInNested(path, selectors[key].nested, targetAttribute)) {
          arr.push(deepcopy(selectors[key]))
        }
      }
    }
  });

  const sortedArray = arr.sort((a, b) => a.nested.length - b.nested.length);
  const classNamesObject = sortedArray.reduce((accumulator, curr) => {
    accumulator = Object.assign({}, accumulator, curr.className)
    return accumulator
  }, {})

  const pseudoObject = sortedArray.reduce((accumulator, curr) => {
    if (curr.pseudo) {
      Object.keys(curr.pseudo).forEach((key) => {
        accumulator[key] = Object.assign({}, accumulator[curr.pseudo[key]], curr.pseudo[key])
      })
    }
    return accumulator;
  }, {});

  const mediaObject = sortedArray.reduce((accumulator, curr) => {
    if (curr.media) {
      Object.keys(curr.media).forEach((key) => {
        accumulator[key] = Object.assign({}, accumulator[curr.media[key]], curr.media[key])
      })
    }
    return accumulator
  }, {});

  const mediaPseudoObject = sortedArray.reduce((accumulator, curr) => {
    if (curr.media && curr.atRulePseudo) {
      Object.keys(curr.atRulePseudo).forEach((key) => {
        accumulator[key] = Object.assign({}, accumulator[curr.atRulePseudo[key]], curr.atRulePseudo[key])
      })
    }
    return accumulator
  }, {});

  return nodeClassNames.concat(
    Object.values(classNamesObject),
    ...Object.keys(pseudoObject).map((key) => Object.values(pseudoObject[key]).map((value) => `${key}:${value}`)),
    ...Object.keys(mediaObject).map((key) => Object.values(mediaObject[key])),
    ...Object.keys(mediaPseudoObject).map((key) => Object.values(mediaPseudoObject[key])),
  ).join(' ')
}
function setConsequentAndAlternateValue({ path, consequent, alternate, targetAttribute, quasisClassNames, selectors }) {
  // consequent：是条件语句（如 if-else 语句）中的一个属性，它表示当条件为真（true）时执行的语句块。换句话说，它对应于 "then" 部分的代码。
  if (consequent && consequent.value) {
    const classNames = consequent.value.split(' ')
    const value = getClassNames(path, classNames, selectors, targetAttribute)
    if (quasisClassNames.length > 0) {
      const newValue = value.split(' ')
      const intersectionSelector = {}
      quasisClassNames.forEach((item) => {
        classNames.forEach((i) => {
          intersectionSelector[`${item}.${i}`] = true
          intersectionSelector[`${i}.${item}`] = true
          const value = getClassNames(path, Object.keys(intersectionSelector), selectors, targetAttribute)
          newValue.push(...value.split(' '))
        })
      })
      consequent.value = newValue.filter((i) => !intersectionSelector[i]).join(' ')
    } else {
      if (classNames.length > 1) {
        // 检查交集
        const intersectionSelector = {}
        intersectionSelector[`${classNames.join('.')}`] = true
        intersectionSelector[`${classNames.reverse().join('.')}`] = true
        const newValue = value.split(' ')
        const mixedValue = getClassNames(path, Object.keys(intersectionSelector), selectors, targetAttribute)
        newValue.push(...mixedValue.split(' '))
        consequent.value = newValue.filter((i) => !intersectionSelector[i]).join(' ')
      } else {
        consequent.value = value
      }
    }
  }
  // alternate（或称为 "alternateConsequent"）：同样在条件语句中，表示当条件为假（false）时执行的语句块。这对应于 "else" 部分的代码。
  if (alternate) {
    if (alternate.value) {
      const classNames = alternate.value.split(' ')
      const value = getClassNames(path, classNames, selectors, targetAttribute)
      const intersectionSelector = {}
      if (quasisClassNames.length > 0) {
        const newValue = value.split(' ')
        quasisClassNames.forEach((item) => {
          classNames.forEach((i) => {
            intersectionSelector[`${item}.${i}`] = true
            intersectionSelector[`${i}.${item}`] = true
            const value = getClassNames(path, Object.keys(intersectionSelector), selectors, targetAttribute)
            newValue.push(...value.split(' '))
          })
        })
        alternate.value = newValue.filter((i) => !intersectionSelector[i]).join(' ')
      } else {
        const value = getClassNames(path, classNames, selectors, targetAttribute)
        const intersectionSelector = {}
        if (classNames.length > 1) {
          // 检查交集
          intersectionSelector[`${classNames.join('.')}`] = true
          intersectionSelector[`${classNames.reverse().join('.')}`] = true
          const newValue = value.split(' ')
          const mixedValue = getClassNames(path, Object.keys(intersectionSelector), selectors, targetAttribute)
          newValue.push(...mixedValue.split(' '))
          alternate.value = newValue.filter((i) => !intersectionSelector[i]).join(' ')
        } else {
          alternate.value = value
        }
      }
    }
    if (alternate.quasis) {
      const identifiers = alternate.expressions?.filter((i) => i.property.type === 'Identifier') || []
      alternate.quasis.forEach((i) => {
        if (i.value.raw) {
          if (identifiers.length === 0) {
            const classNames = i.value.raw.split(' ').filter(Boolean)
            quasisClassNames.push(...classNames)
            const value = getClassNames(path, classNames, selectors, targetAttribute)
            i.value.raw = ` ${value} `
          }
          if (identifiers.length === 1) {
            const endWiths = i.end + 2 === identifiers[0].start
            const startWiths = i.start - 1 === identifiers[0].end
            if (endWiths) {
              const classNames = i.value.raw.split(' ').filter(Boolean).slice(0, -1)
              quasisClassNames.push(...classNames)
              const value = getClassNames(path, classNames, selectors, targetAttribute)
              i.value.raw = ` ${value} ${i.value.raw.split(' ').slice(-1)}`
            } else if (startWiths) {
              const classNames = i.value.raw.split(' ').filter(Boolean)
              quasisClassNames.push(...classNames)
              const value = getClassNames(path, classNames, selectors, targetAttribute)
              i.value.raw = ` ${value}`
            }
          }
        }
      })
      if (identifiers.length > 0) {
        identifiers.forEach((i) => {
          console.warn(`${JSON.stringify(i)} cant not to converted to tailwindcss  at ${root.source.input.file}. at ${filePath} \n`)
        })
      }
    }
  }
}
exports.accessFile = accessFile;
exports.accessFileSync = accessFileSync
exports.accessFileSyncfromCache = accessFileSyncfromCache
exports.isInAtRule = isInAtRule
exports.isInNested = isInNested
exports.isPseudoRule = isPseudoRule
exports.getValue = getValue;
exports.getRealValue = getRealValue;
exports.generateCSSRule = generateCSSRule
exports.getClassNames = getClassNames;
exports.getPseudoClassName = getPseudoClassName
exports.clearEmptyClassName = clearEmptyClassName
exports.getTailwindClassName = getTailwindClassName;
exports.getNestedSelectorsAndSelectorKey = getNestedSelectorsAndSelectorKey
exports.setConsequentAndAlternateValue = setConsequentAndAlternateValue
